package br.com.grimoire.calc;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;

public class CalcModelImpl implements CalcModel {
	private BigDecimal output;
	private BigDecimal lastInput;
	private static final int SCALE = 32;
	private static final MathContext context = new MathContext(32, RoundingMode.HALF_UP);
	private int operator;
	
	public CalcModelImpl() {
		init();
	}

	@Override
	public void init() {
		output = new BigDecimal(0);
		lastInput = null;
		operator = CLEAR;
	}

	@Override
	public BigDecimal process(BigDecimal input, int action) 
			throws ArithmeticException {
		switch (action) {
			case CLEAR:
				init();
				break;
			case EQUAL:
				if (lastInput != null) {
					output = calculate(output, operator, lastInput);
					break;
				} 
			case ADD:
			case SUBTRACT:
			case MULTIPLY:
			case DIVIDE:			
				if (operator != CLEAR) {
					output = calculate(output, operator, input);
					lastInput = null;
				} else {
					output = input;
				}
				operator = (action == EQUAL) ? CLEAR : action;
				break;
			default:
				init();
				throw new RuntimeException("Operation not suported.");
		}
		
		return output;
	}

	@Override
	public Double process(Double input, int action) {
		return process(new BigDecimal(input), action).doubleValue();
	}
	
	@Override
	public BigDecimal process(int action) 
			throws ArithmeticException {
		switch (action) {
			case CLEAR:
				init();
				break;
			case ADD:
			case SUBTRACT:
			case MULTIPLY:
			case DIVIDE:
				operator = action;
				break;
			case EQUAL:
				if (operator == ADD
						|| operator == SUBTRACT
						|| operator == MULTIPLY
						|| operator == DIVIDE) {
					if (lastInput == null) {
						lastInput = output;
					}
					output = calculate(output, operator, lastInput);
				}
				break;
			default:
				init();
				throw new RuntimeException("Operation not suported.");
		}
		
		return output;
	}

	private BigDecimal calculate(BigDecimal operand1, int operator, BigDecimal operand2) 
			throws ArithmeticException {
		BigDecimal result;
		switch (operator) {
			case ADD :
				result = operand1.add(operand2);
				break;
			case SUBTRACT :
				result = operand1.subtract(operand2);
				break;
			case MULTIPLY :
				result = operand1.multiply(operand2);
				break;
			case DIVIDE :				
				try {
					result = operand1.divide(operand2);
				} catch (ArithmeticException e) {
					result = operand1.divide(operand2, context); // TODO correct scale ??
				}				
				break;
			default :
				init();
				throw new RuntimeException("Operation not suported.");
		}		
		return result;
	}


	

}
